package com.m.sms.aspect;


import cn.hutool.core.util.StrUtil;
import com.m.sms.annotation.SmsLimit;
import com.m.sms.response.SmsSendResponseBody;
import com.m.sms.response.SmsSendStatus;
import com.m.sms.enums.SmsStatusEnum;
import com.m.sms.properties.SmsProperties;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Set;

/**
 * Description
 * 发送单个消息是校验频率是否超出限制
 * @Auther [li.wang@jsxfedu.com]
 * @Date 2025/3/20 17:49
 * @Version 1.0
 * @Copyright (c) 2022 京师讯飞
 */
@Slf4j
@Aspect
@Component
public class SmsLimitAspect {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private SmsProperties smsProperties;

    private final String LIMIT_KEY_FORMAT = "sms:limit:%s";

    @Around("@annotation(com.m.sms.annotation.SmsLimit)")
    @SneakyThrows
    public Object limit(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        SmsLimit smsLimit = method.getAnnotation(SmsLimit.class);
        // 获取方法参数
        String expression = smsLimit.value();
        Object[] args = joinPoint.getArgs();
        String[] parameterNames = signature.getParameterNames();
        StandardEvaluationContext context = new StandardEvaluationContext();
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }
        // 解析 SpEL 表达式
        ExpressionParser parser = new SpelExpressionParser();
        Expression exp = parser.parseExpression(expression);
        String mobile = exp.getValue(context, String.class);

        Long now = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);
        String key = String.format(LIMIT_KEY_FORMAT, mobile);
        String msg = validSmsFrequency(mobile);
        Object result;
        if(StrUtil.isEmpty(msg)) {
            result = joinPoint.proceed();
            if(StrUtil.isNotEmpty(mobile)) {
                // 设置合理过期时间
                redisTemplate.opsForZSet().add(key, now, now);
                redisTemplate.expire(key, Duration.ofDays(1));
            }
        }else{
            SmsSendResponseBody smsSendResponseBody = new SmsSendResponseBody();
            smsSendResponseBody.setTotalCount(1);
            smsSendResponseBody.setFailCount(1);
            SmsSendStatus smsSendStatus = SmsSendStatus.builder()
                    .status(SmsStatusEnum.FAIL)
                    .mobile(mobile)
                    .msg(msg).build();
            smsSendResponseBody.getSendStatusList().add(smsSendStatus);
            result = smsSendResponseBody;
        }
        return result;
    }


    /**
     * 校验短信发送频率
     * @param mobile
     * @return
     */
    private String validSmsFrequency(String mobile) {
        if(StrUtil.isEmpty(mobile)) {
            return null;
        }
        Long now = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);

        String key = String.format(LIMIT_KEY_FORMAT, mobile);
        long oneMinuteAgo = now - Duration.ofMinutes(1).getSeconds();
        long oneHourAgo = now - Duration.ofHours(1).getSeconds();
        long twentyFourHoursAgo = now - Duration.ofDays(1).getSeconds();
        // 清理过期记录
        redisTemplate.opsForZSet().removeRangeByScore(key, 0, twentyFourHoursAgo);

        Set<Integer> list = redisTemplate.opsForZSet().range(key, 0, -1);

        if(list.stream().filter(o -> o.longValue() > oneMinuteAgo).count() > smsProperties.getLimitMinute()){
            return String.format ("短信发送太频繁，请%s后再试", getHumanReadableDuration(Duration.ofMinutes(1)));
        }else if(list.stream().filter(o -> o.longValue() > oneHourAgo).count() > smsProperties.getLimitHour()){
            return String.format ("短信发送太频繁，请%s后再试", getHumanReadableDuration(Duration.ofHours(1)));
        }else if(list.stream().filter(o -> o.longValue() > twentyFourHoursAgo).count() > smsProperties.getLimit24Hour()){
            return String.format ("短信发送太频繁，请%s后再试", getHumanReadableDuration(Duration.ofDays(1)));
        }
        return null;
    }


    private String getHumanReadableDuration(Duration duration) {
        if (duration.getSeconds() < 60) {
            return "1 分钟";
        } else if (duration.getSeconds() < 3600) {
            return String.format("% d 分钟", duration.getSeconds() / 60);
        } else {
            return String.format("% d 小时", duration.getSeconds() / 3600);
        }
    }
}
